#pragma once

#include "marusdefs.h"
#include <intrin.h>

namespace marus
{

template <size_t type_size>
struct interlocked
{
};

template <>
struct interlocked<4>
{
    template<typename T>
    static FORCEINLINE volatile long& rcast(volatile T& ref_)
    {
        _STATIC_ASSERT(sizeof(T) == 4);
        return reinterpret_cast<volatile long&>(ref_);
    }

    template<typename T>
    static FORCEINLINE long vcast(T value_)
    {
        _STATIC_ASSERT(sizeof(T) == 4);
        return *reinterpret_cast<long*>(&value_);
    }

    template<typename T>
    static FORCEINLINE T uncast(long value_)
    {
        return *reinterpret_cast<T*>(&value_);
    }

    // get
    // set
    // compare_exchange

    template<typename T>
    static INLINE T FASTCALL get(volatile T& ref_)
    {
        _STATIC_ASSERT(sizeof(T) == 4);
        return uncast<T>(_InterlockedExchangeAdd(&rcast(ref_), 0));
    }

    template<typename T>
    static INLINE T FASTCALL set(volatile T& ref_, T value_)
    {
        _STATIC_ASSERT(sizeof(T) == 4);
        return uncast<T>(_InterlockedExchange(&rcast(ref_), vcast(value_)));
    }

    template<typename T>
    static INLINE T FASTCALL compare_exchange(volatile T& ref_, T value_, T comparand_)
    {
        _STATIC_ASSERT(sizeof(T) == 4);
        return uncast<T>(_InterlockedCompareExchange(&rcast(ref_), vcast(value_), vcast(comparand_)));
    }
};

struct  interlocked32
{
    static INLINE long FASTCALL get(volatile long& ref_)
    {
        return _InterlockedExchangeAdd(&ref_, 0);
    }

    static INLINE long FASTCALL set(volatile long& ref_, long value_)
    {
        return _InterlockedExchange(&ref_, value_);
    }

    static INLINE long FASTCALL add(volatile long& ref_, long value_)
    {
        return _InterlockedExchangeAdd(&ref_, value_);
    }

    static INLINE long FASTCALL and(volatile long& ref_, long value_)
    {
        return _InterlockedAnd(&ref_, value_);
    }

    static INLINE long FASTCALL or(volatile long& ref_, long value_)
    {
        return _InterlockedOr(&ref_, value_);
    }

    static INLINE long FASTCALL xor(volatile long& ref_, long value_)
    {
        return _InterlockedXor(&ref_, value_);
    }

    static INLINE long FASTCALL compare_exchange(volatile long& ref_, long value_, long comparand_)
    {
        return _InterlockedCompareExchange(&ref_, value_, comparand_);
    }

    static INLINE long FASTCALL increment(volatile long& ref_)
    {
        return _InterlockedIncrement(&ref_);
    }

    static INLINE long FASTCALL decrement(volatile long& ref_)
    {
        return _InterlockedDecrement(&ref_);
    }

    template <const long _Z>
    static bool FASTCALL increment_ne(volatile long& ref_)
    {	// increment ref_ if ref_ != _Z, return true if successful
        for (long val = ref_; val != _Z; val = ref_)
        {	
            if (compare_exchange(ref_, val + 1, val) == val)
                return (true);
        }
        return (false);
    }

    static INLINE bool FASTCALL increment_nz(volatile long& ref_)
    {   // increment if not zero, return true if successful
        return increment_ne<0>(ref_);
    }

    template <const long _Z>
    static bool FASTCALL decrement_ne(volatile long& ref_)
    {	// increment if not zero, return true if successful
        for (long val = ref_; val != _Z; val = ref_)
        {	
            if (compare_exchange(ref_, val - 1, val) == val)
                return (true);
        }
        return (false);
    }

    static INLINE bool FASTCALL decrement_nz(volatile long& ref_)
    {
        return decrement_ne<0>(ref_);
    }

};

/*
todo:
struct interlocked<8>
{
    ::InterlockedExchangeAdd64(&i64, 100);
    ::InterlockedIncrement64(&i64);
    ::InterlockedDecrement64(&i64);
    ::InterlockedCompareExchange64(&i64, 200, 100);
    ::InterlockedOr64(&i64, 1);
    ::InterlockedAnd64(&i64, 0x55);
    ::InterlockedXor64(&i64, 0x55);
}

struct interlocked64
{
};
*/


class   atomic_long
{
private:
    mutable volatile long    _value;
public:

    atomic_long(long nInitial = 0)
        : _value(nInitial)
    {
        // must be aligned on a 32-bit boundary
        _ASSERT(PROPERLY_ALIGNED(_value));
    }

    INLINE 
    long FASTCALL value() const
    {
        return interlocked32::get(_value);
    }

    INLINE 
    FASTCALL operator long() const
    {
        return value();
    }

    INLINE
    long FASTCALL set(long value_)
    {
        return interlocked32::set(_value, value_);
    }

    INLINE
    long    FASTCALL add(long value_)
    {
        return interlocked32::add(_value, value_);
    }

    INLINE
    long    FASTCALL and(long value_)
    {
        return interlocked32::and(_value, value_);
    }

    INLINE
    long    FASTCALL or(long value_)
    {
        return interlocked32::or(_value, value_);
    }

    INLINE
    long    FASTCALL xor(long value_)
    {
        return interlocked32::xor(_value, value_);
    }

    INLINE
    long FASTCALL operator=(long value_)
    {
        set(value_);
        return value();
    }

    INLINE
    long    FASTCALL operator += (long value_)
    {
        add(value_);
        return value(); 
    }

    INLINE
    long    FASTCALL operator -= (long value_)
    {
        add(-value_);
        return value(); 
    }

    INLINE
    long    FASTCALL operator &= (long value_)
    {
        and(value_);
        return value(); 
    }

    INLINE
    long    FASTCALL operator |= (long value_)
    {
        or(value_);
        return value(); 
    }

    INLINE
    long    FASTCALL operator ^= (long value_)
    {
        xor(value_);
        return value(); 
    }

    INLINE
    long    FASTCALL operator++()
    {
        return increment();
    }

    INLINE
    long    FASTCALL operator++(int)
    {
        return add(1L);
    }

    INLINE
    long    FASTCALL operator--()
    {
        return decrement();
    }

    INLINE
    long    FASTCALL operator--(int)
    {
        return add(-1L);
    }

    INLINE
    long FASTCALL increment()
    {
        return interlocked32::increment(_value);
    }

    INLINE
    long FASTCALL decrement()
    {
        return interlocked32::decrement(_value);
    }
};

}